【CS61B】Lec19-20 Hashing

  • ~1.64K 字
  1. 1. Lec19-20 Hashing
    1. 1.1. 搜索树的可进步之处
    2. 1.2. 快一点
      1. 1.2.1. WriteItOnTheWall Set
      2. 1.2.2. BobaCounter Set
    3. 1.3. 哈希表
      1. 1.3.1. 减少浪费空间
      2. 1.3.2. 处理大量数据
      3. 1.3.3. 取模操作
      4. 1.3.4. 增加M
      5. 1.3.5. 支持其他数据类型
      6. 1.3.6. 哈希码
      7. 1.3.7. 哈希表
      8. 1.3.8. 哈希函数
    4. 1.4. 不可变类型

Lec19-20 Hashing

搜索树的可进步之处

  • 树中的元素需要是可比较的,如两个汉字便无法存储在其中
  • 时间复杂度为,能不能再进行优化

快一点

WriteItOnTheWall Set

假设我们有一个集合WriteItOnTheWall

对于这种集合,添加元素是很快的,只要有空位就可以添加,但是寻找元素是很慢的

用时间复杂度表示为与

这种集合的好处是不需要管元素种类,只管往里添加即可

BobaCounter Set

我们将上面的集合优化,将整面墙分为十个部分,标号0-9,对应每个数字的个位数放入相应的集合中。

这样我们的耗时可以减少十分之一

但是这种思路也有问题:

  • 会浪费每个分区的空间
  • 各个分区元素个数可能会不平衡
  • 数字不一定随机
  • 其他数据类型该如何处理

哈希表

减少浪费空间

使用链表,每个相同尾数的元素进入后添加一个节点链接到上一个元素后面

处理大量数据

设有N个物品,M个桶,假设平均分,则每个桶里有个物品,时间复杂度为

若M为常数则时间复杂度可以减至

使M随着N的增长而增长,我们可以做到常数时间复杂度。这时我们需要一种方法来根据不同的M将数组分为M组

取模操作

我们可以通过取模做到这一点

  • 可以适用于任意的M
  • 可以平均分配随机的数
  • 使用质数当模数分配结果可以更加独立

增加M

为了保持常数时间复杂度,我们需要始终保持小于某个常数k

常用方法是:当桶的平均大小大于k,我们增加M

  • 当增加M时,我们同时要保证将数重新均分到新的桶中
  • 参考ArrayList中的resize,最好的方法是每次double M的大小
过大 扩容

综上,若我们有N个元素均分,每个列表的长度为

  • 近乎一个常数
  • 故操作的时间复杂度也大约是常数

支持其他数据类型

对于不同数据类型,问题在于存放数据的集合并不负责将对应数据分类。

集合对于int类型的操作最为灵活,故我们需要让集合只作用与int类型

此时其他类型需要依靠自己的类来实现到int的转换

对字符串:有一种方法是将字符串用26进制转换为一个数

转换例

通过这种方法我们可以支持string操作

哈希码

对于汉字等拥有庞大字库的字符,若使用unicode码套用该方法,转换出的数字可能会超出数据类型的范围,实际上数字大小随着字符长度是指数增加的

因此我们需要通过减小进制数来将庞大的数字固定在一个范围内,如整型的取值范围等,如java中使用31计算,计算出来的数值即为哈希码,减小范围的代价是可能有重复的哈希码,但是对于有意义的字符串,这种概率还是比较小的

Java转换哈希码的函数如下:

哈希表

我们创建的集合称为哈希表

  • 数据通过特定的哈希函数转换成哈希码,保证在整型的取值范围内
  • 哈希码之后使用模数被减少到一个合理的下标

在Java中,哈希表最常用来实现set和map

哈希函数

设计哈希函数的原则是让入表的元素平均分配

其中一种重写哈希函数的场景是对类重写equals方法时,为了能让哈希表正常工作,我们也需要重写哈希码方法,来确保若两个元素相等则他们的哈希码一致,若不设置可能相同的元素会进入不同的桶中

不可变类型

不可变类型指在实例化后无法再改变的类型

如int,string等类型

final关键字可以设置一个不可变变量,防止你在设置值后进行修改

  • final变量意味着你可以给该变量分配一个值,这之后就不能再修改了
  • final关键字对于一个不可变类来说不是必须的
不可变类

哈希表中,不要改变一个用作键的对象,否则会将自己放在错误的位置

赞助喵
非常感谢您的喜欢!
赞助喵